/* Gluten Library -- Vector
 *
 * A templated vector (the Euclidean kind, not the container) type. Supports
 * arbitrary dimensions, although some functionality only supports 2- and
 * 3-dimension vectors.
 *
 * $AUTHOR$    res
 * $UPDATE$    r32
 */

/* LICENSES ********************************************************************

Copyright (c) 2013- Reuben E. Smith

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*******************************************************************************/

#ifndef GLUTEN_VECTOR_HPP
#define GLUTEN_VECTOR_HPP

// Uncomment this if you need speed more than accuracy.
//#define VECTOR_FAST_MATH
// Uncomment this if you do not want the SIMD specialization of Vector4f.
//#define VECTOR_NO_SIMD

#ifndef VECTOR_NO_SIMD
   #ifdef _MSC_VER
      #define V_ALIGN __declspec(align(16))
   #else
      // res: This is assuming everything else is GCC. True in my case, but
      //      this will need changing for even more cross-platform-y-ness.
      #define V_ALIGN __attribute__((aligned(16)))
   #endif
#endif


namespace Gluten
{
   class VectorTests;

   //! \class Vector
   //! \brief Describes a vector with N dimensions and storage type T.
   //! \param[in]    N     number of coordinate dimensions
   //! \param[in]    T     storage type for dimensions
   template < unsigned int N, typename T >
   class Vector
   {
   public:
      typedef T value_t;
      static const unsigned int dimensions = N;


      //! \brief Default constructor. Zeros out all dimensions.
      Vector();

      //! \brief Copy constructor.
      //! \param[in]    other          vector to copy
      Vector(const Vector& other);

      //! \brief Parametric constructor. Uses an array to initialize values.
      //! \param[in]    values         dimension value array
      template < typename U >
      explicit Vector(const U values[N]);

      //! \brief Assignment operator. Copies another vector's values.
      //! \param[in]    other          the vector copied
      //! \return       this vector
      Vector& operator=(const Vector& other);

      //! \brief Test if this vector is value equivalent to another.
      //! \param[in]    other          the vector compared to
      //! \return       true iff vectors are equal
      bool operator==(const Vector& other) const;

      //! \brief Test if this vector is not value equivalent to another.
      //! \param[in]    other          the vector compared to
      //! \return       true iff vectors are not equal
      bool operator!=(const Vector& other) const;

      //! \brief Read-only accessor. Get const reference to dimension.
      //! \param[in]    dim            the desired dimension
      //! \return       a const reference to that dimension
      const T& operator()(unsigned int dim) const;

      //! \brief Read and write accessor. Get reference to dimension.
      //! \param[in]    dim            the desired dimension
      //! \return       a reference to that dimension
      T& operator()(unsigned int dim);

      //! \brief Get the squared norm of this vector, also known as the length.
      //! \return       the squared norm of this vector
      //!
      //! Get the squared norm of this vector. The norm of a vector is the dot
      //! product of a vector with itself, which is the same thing as its 
      //! length. The squared norm is given to allow for faster comparisons 
      //! that just need proportionality and not the actual norm.
      T normSquare() const;

      //! \brief Get the norm of this vector, also known as the magnitude.
      //! \return       the norm of this vector
      T norm() const;

      //! \brief Get a normalized copy of this vector.
      //! \return       a new vector, the normalized version of this vector
      Vector normal() const;

      //! \brief Normalizes this vector.
      //! \return       this vector, normalized
      Vector& normalize();

      //! \brief Get the dot product of this and another vector.
      //! \param[in]    other          the vector to find the product with
      //! \return       the dot product of these two vectors
      T dot(const Vector& other) const;

      //! \brief Get the cross product of this and another vector.
      //! \param[in]    other          the vector to find the product with
      //! \return       a new vector, the cross product of these two
      //!
      //! Get the cross product of two vectors. As the cross product is only
      //! well defined for 3-dimensional vectors, this will return an empty 
      //! vector of the same number of dimensions as this one if N != 3.
      Vector cross(const Vector& other) const;

      //! \brief Clone operator.
      //! \return       a copy of this vector
      Vector operator+() const;
      
      //! \brief Add two vectors component-wise.
      //! \param[in]    other          vector to add
      //! \return       a new, sum vector
      Vector operator+(const Vector& other) const;

      //! \brief Add a scalar to a vector component-wise.
      //! \param[in]    s              scalar value to add
      //! \return       a new, sum vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator+(S s) const;

      //! \brief Add a scalar-vector and vector component-wise.
      //! \param[in]    s              scalar-vector to add to
      //! \param[in]    v              vector to add
      //! \return       a new, sum vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator+(S s, const Vector& v)
      {
         Vector<N, T> r;

         for (unsigned int i = 0; i < N; ++i) {
            r._d[i] = s + v._d[i];
         }

         return r;
      }


      //! \brief Add a vector to this vector.
      //! \param[in]    other          vector to add
      //! \return       this vector
      Vector& operator+=(const Vector& other);

      //! \brief Add a scalar to this vector component-wise.
      //! \param[in]    s              scalar value to add
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator+=(S s);

      //! \brief Get negated vector.
      //! \return       a new, negated instance of this vector
      Vector operator-() const;

      //! \brief Subtract two vectors component-wise.
      //! \param[in]    other          vector to subtract
      //! \return       a new, differenced vector
      Vector operator-(const Vector& other) const;

      //! \brief Subtract a scalar from a vector component-wise.
      //! \param[in]    s              scalar value to subtract
      //! \return       a new, differenced vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator-(S s) const;

      //! \brief Subtract a scalar-vector and vector component-wise.
      //! \param[in]    s              scalar-vector to subtract from
      //! \param[in]    v              vector to subtract
      //! \return       a new, differenced vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator-(S s, const Vector& v)
      {
         Vector<N, T> r;

         for (unsigned int i = 0; i < N; ++i) {
            r._d[i] = s - v._d[i];
         }

         return r;
      }


      //! \brief Subtract a vector from this vector.
      //! \param[in]    other          vector to subtract
      //! \return       this vector
      Vector& operator-=(const Vector& other);

      //! \brief Subtract a scalar from this vector.
      //! \param[in]    s              scalar value to subtract
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator-=(S s);

      //! \brief Multiply two vectors component-wise.
      //! \param[in]    other             vector to multiply by
      //! \return       a new, scaled vector
      Vector operator*(const Vector& other) const;

      //! \brief Multiply a vector component-wise by a scalar.
      //! \param[in]    s              scalar value to multiply by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator*(S s) const;

      //! \brief Multiply a scalar-vector and a vector component-wise.
      //! \param[in]    s              scalar-vector to multiply
      //! \param[in]    v              vector to multiply by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator*(S s, const Vector& v)
      {
         Vector<N, T> r;

         for (unsigned int i = 0; i < N; ++i) {
            r._d[i] = s * v._d[i];
         }

         return r;
      }


      //! \brief Multiply this vector component-wise by another.
      //! \param[in]    other             vector to multiply by
      //! \return       this vector
      Vector& operator*=(const Vector& other);

      //! \brief Multiply this vector by a scalar.
      //! \param[in]    s              scalar value to multiply by
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator*=(S s);

      //! \brief Divide two vectors component-wise.
      //! \param[in]    other             vector to divide by
      //! \return       a new, scaled vector
      Vector operator/(const Vector& other) const;

      //! \brief Divide a vector component-wise by a scalar.
      //! \param[in]    s              scalar value to divide by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator/(S s) const;

      //! \brief Divide a scalar-vector and a vector component-wise.
      //! \param[in]    s              scalar-vector to divide
      //! \param[in]    v              vector to divide by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator/(S s, const Vector& v)
      {
         Vector<N, T> r;

         for (unsigned int i = 0; i < N; ++i) {
            r._d[i] = s / v._d[i];
         }

         return r;
      }
      

      //! \brief Divide this vector component-wise by another.
      //! \param[in]    other             vector to divide by
      //! \return       this vector
      Vector& operator/=(const Vector& other);

      //! \brief Divide this vector by a scalar.
      //! \param[in]    s              scalar value to divide by
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator/=(S s);


   private:
      friend class VectorTests;

      T _d[N];
   };


#ifndef VECTOR_NO_SIMD
   //! \class Vector4f
   //! \brief Describes a 4-vector of floats optimized with SIMD.
   template <>
   class Vector<4, float>
   {
   public:
      typedef float value_t;
      static const unsigned int dimensions = 4;


      //! \brief Default constructor.
      explicit Vector();

      //! \brief Vector copy constructor.
      //! \param[in]    other          vector to copy
      Vector(const Vector& other);

      //! \brief Array copy constructor.
      //! \param[in]    values         array to copy
      //!
      //! Note that this assumes `values` can be interpreted as a float array.
      //! Undefined behavior may occur if it cannot be cast.
      template < typename U >
      explicit Vector(U values[4]);

      //! \brief Parametric constructor.
      //! \param[in]  x              x-coordinate
      //! \param[in]  y              y-coordinate
      //! \param[in]  z              z-coordinate
      //! \param[in]  w              w-coordinate
      //!
      //! Note that this assumes that the parameters can be interpreted as
      //! floats. Undefined behavior may occur if they cannot be cast.
      Vector(float x, float y, float z, float w);

      //! \brief Assignment operator.
      //! \param[in]    other          vector to copy
      //! \return       this vector
      Vector& operator=(const Vector& other);

      //! \brief Test if two vectors are equal.
      //! \param[in]    other          vector to test
      //! \return       true if vectors are relatively equal
      bool operator==(const Vector& other) const;

      //! \brief Test if two vectors are unequal.
      //! \param[in]    other          vector to test
      //! \return       true if vectors are relatively unequal
      bool operator!=(const Vector& other) const;

      //! \brief Read-only accessor. Get const reference to dimension.
      //! \param[in]    dim            the desired dimension
      //! \return       value of that dimension
      float operator()(unsigned int dim) const;

      //! \brief Read and write accessor. Get reference to dimension.
      //! \param[in]    dim            the desired dimension
      //! \return       a reference to that dimension
      float& operator()(unsigned int dim);

      //! \brief Get the squared norm of this vector.
      //! \return       the squared norm of this vector
      //!
      //! Note that this is also called length or magnitude.
      float normSquare() const;

      //! \brief Get the norm of this vector.
      //! \return       the norm of this vector
      //!
      //! Note that this is also called length or magnitude.
      float norm() const;

      //! \brief Get a normal of this vector.
      //! \return       a normalized instance of this vector
      Vector normal() const;

      //! \brief Normalize this vector.
      //! \return       this vector
      Vector& normalize();

      //! \brief Get the dot product of two vectors.
      //! \param[in]    other          the vector to take the product of
      //! \return       the dot product of these two vectors
      float dot(const Vector& other) const;

      //! \brief Get the cross product of two vectors.
      //! \param[in]    other          the vector to take the cross of
      //! \return       the cross product of these two vectors
      //!
      //! Note that this retains the original value for w.
      Vector cross(const Vector& other) const;

      //! \brief Clone operator.
      //! \return       a copy of this vector
      Vector operator+() const;
      
      //! \brief Add two vectors component-wise.
      //! \param[in]    other          vector to add
      //! \return       a new, sum vector
      Vector operator+(const Vector& other) const;

      //! \brief Add a scalar to a vector component-wise.
      //! \param[in]    s              scalar value to add
      //! \return       a new, sum vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator+(S s) const;

      //! \brief Add a scalar-vector and vector component-wise.
      //! \param[in]    s              scalar-vector to add to
      //! \param[in]    v              vector to add
      //! \return       a new, sum vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator+(S s, const Vector& v);

      //! \brief Add a vector to this vector.
      //! \param[in]    other          vector to add
      //! \return       this vector
      Vector& operator+=(const Vector& other);

      //! \brief Add a scalar to this vector component-wise.
      //! \param[in]    s              scalar value to add
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator+=(S s);

      //! \brief Get negated vector.
      //! \return       a new, negated instance of this vector
      Vector operator-() const;

      //! \brief Subtract two vectors component-wise.
      //! \param[in]    other          vector to subtract
      //! \return       a new, differenced vector
      Vector operator-(const Vector& other) const;

      //! \brief Subtract a scalar from a vector component-wise.
      //! \param[in]    s              scalar value to subtract
      //! \return       a new, differenced vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator-(S s) const;

      //! \brief Subtract a scalar-vector and vector component-wise.
      //! \param[in]    s              scalar-vector to subtract from
      //! \param[in]    v              vector to subtract
      //! \return       a new, differenced vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator-(S s, const Vector& v);

      //! \brief Subtract a vector from this vector.
      //! \param[in]    other          vector to subtract
      //! \return       this vector
      Vector& operator-=(const Vector& other);

      //! \brief Subtract a scalar from this vector.
      //! \param[in]    s              scalar value to subtract
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator-=(S s);

      //! \brief Multiply two vectors component-wise.
      //! \param[in]    other             vector to multiply by
      //! \return       a new, scaled vector
      Vector operator*(const Vector& other) const;

      //! \brief Multiply a vector component-wise by a scalar.
      //! \param[in]    s              scalar value to multiply by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator*(S s) const;

      //! \brief Multiply a scalar-vector and a vector component-wise.
      //! \param[in]    s              scalar-vector to multiply
      //! \param[in]    v              vector to multiply by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator*(S s, const Vector& v);

      //! \brief Multiply this vector component-wise by another.
      //! \param[in]    other             vector to multiply by
      //! \return       this vector
      Vector& operator*=(const Vector& other);

      //! \brief Multiply this vector by a scalar.
      //! \param[in]    s              scalar value to multiply by
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator*=(S s);

      //! \brief Divide two vectors component-wise.
      //! \param[in]    other             vector to divide by
      //! \return       a new, scaled vector
      Vector operator/(const Vector& other) const;

      //! \brief Divide a vector component-wise by a scalar.
      //! \param[in]    s              scalar value to divide by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector operator/(S s) const;

      //! \brief Divide a scalar-vector and a vector component-wise.
      //! \param[in]    s              scalar-vector to divide
      //! \param[in]    v              vector to divide by
      //! \return       a new, scaled vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      friend Vector operator/(S s, const Vector& v);

      //! \brief Divide this vector component-wise by another.
      //! \param[in]    other             vector to divide by
      //! \return       this vector
      Vector& operator/=(const Vector& other);

      //! \brief Divide this vector by a scalar.
      //! \param[in]    s              scalar value to divide by
      //! \return       this vector
      //!
      //! Note that this assumes `s` can be interpreted as a float. Undefined
      //! behavior may occur if it cannot be cast.
      template < typename S >
      Vector& operator/=(S s);


   private:
      friend class VectorTests;

   #ifdef _MSC_VER
      V_ALIGN char _d_raw[4 * sizeof(float) + 16];
              float* _d;
   #else
      V_ALIGN float _d[4];
   #endif

      V_ALIGN mutable float _s;
   };
#endif


   // Vectors aren't points and I like to distinguish between them when it's
   // meaningful to do so. However, I'm lazy and don't want to copy so much code
   // just to create a new point type, so... just don't use vector-specific 
   // methods and we're golden, yes?

   // types for convience
   typedef Vector<2, int> Vector2i;
   typedef Vector<2, unsigned int> Vector2ui;
   typedef Vector<3, int> Vector3i;
   typedef Vector<3, unsigned int> Vector3ui;
   typedef Vector<2, float> Vector2f;           // SIMD candidate?
   typedef Vector<3, float> Vector3f;           // SIMD candidate?
   typedef Vector<2, double> Vector2d;
   typedef Vector<3, double> Vector3d;
   typedef Vector<4, float> Vector4f;           // has SIMD

#ifndef _MSC_VER
   template < unsigned int N, typename T >
   using Point = Vector<N, T>;

   typedef Point<2, int> Point2i;
   typedef Point<2, unsigned int> Point2ui;
   typedef Point<3, int> Point3i;
   typedef Point<3, unsigned int> Point3ui;
   typedef Point<2, float> Point2f;
   typedef Point<3, float> Point3f;
   typedef Point<2, double> Point2d;
   typedef Point<3, double> Point3d;
#else
   typedef Vector<2, int> Point2i;
   typedef Vector<2, unsigned int> Point2ui;
   typedef Vector<3, int> Point3i;
   typedef Vector<3, unsigned int> Point3ui;
   typedef Vector<2, float> Point2f;
   typedef Vector<3, float> Point3f;
   typedef Vector<2, double> Point2d;
   typedef Vector<3, double> Point3d;
#endif
}

#include "Vector.tpp"

#endif

